import cgi
import os
from google.appengine.ext import db
from re import match


class Sequence(db.Model):
	current = db.IntegerProperty(required=True)
	referer_restriction = db.StringProperty()
	requester_restriction = db.StringProperty()


def transaction(output):
	req_method = os.environ["REQUEST_METHOD"]
	req_path = os.environ["PATH_INFO"]
	
	assert match("^/\w+\.js$",req_path), "No funny characters in path, please."
	sequence = Sequence.get_by_key_name(req_path)
	
	form = cgi.FieldStorage()
	callback = form.getfirst("callback","callback")
	assert match("^\w+$",callback), "No funny characters in callback, please."
	
	if (req_method == "GET"):
		if (sequence == None):
			result(output,callback,"")
		else:
			result(output,callback,sequence.current)
	elif (req_method == "POST"):
		if (sequence == None):
			start = int(form.getfirst("start","1"))
			new_sequence = Sequence(
				key_name = req_path,
				current = start,
				referer_restriction = form.getfirst("referer",""),
				requester_restriction = form.getfirst("requester","")
			)
			validate(new_sequence)
			new_sequence.put()
			result(output,callback,start)
		else:
			validate(sequence)
			sequence.current += 1
			sequence.put()
			result(output,callback,sequence.current)
	else:
		raise "Only GET and POST are supported."


def validate(sequence):
	assert referer().startswith(sequence.referer_restriction) and \
		requester().endswith(sequence.requester_restriction), \
		"Expected referer ('%s') to start with '%s'. " % (referer(), sequence.referer_restriction) + \
		"Expected requester ('%s') to end with '%s'." % (requester(), sequence.requester_restriction)
	
def result(output,callback,count):
	output.append("%s(%s);" % (callback,count))
	
def referer():
	return os.environ.get("HTTP_REFERER","")

def requester():
	return os.environ.get("REMOTE_HOST",os.environ.get("REMOTE_ADDR",""))

output = ["Content-Type: text/javascript","",
"""/*
Normal usage: (replace "example" with an identifier of your choice.)

GET example.js
	Returns "callback();" with no arguments if a sequence is not yet associated with "example".
POST example.js
	Creates a sequence and returns "callback(1);".
POST example.js
	Increments, and returns "callback(2);".
GET example.js
	Does not increment, but returns the current value: "callback(2);".

Parameters:
	both GET and POST:
		callback: A JSONP callback. This is not stored. (Optional: defaults to "callback".)
	POST only, and only when creating a new sequence:
		start: The integer to start at. (Optional: defaults to 1.)
		referer: The referer headers of future POSTs must begin with this. (Optional)
		requester: The requester address in all future POSTs must end with this. (Optional)
*/""",
"// for debugging: referer: '%s' requester: '%s'" % (referer(),requester())]
db.run_in_transaction(transaction,output)
print "\n".join(output)
